package top.lingkang.mm;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.session.*;
import top.lingkang.mm.transaction.TransactionManage;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.util.List;
import java.util.Map;

/**
 * SqlSession 的代理实现
 *
 * @Author lingkang
 * @Date 2024/2/29 13:52
 */
@Slf4j
public class MagicSqlSession implements SqlSession {
    private SqlSessionFactory sqlSessionFactory;
    private ExecutorType executorType;
    private SqlSession sessionProxy;


    public MagicSqlSession(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
        this.sqlSessionFactory = sqlSessionFactory;
        this.executorType = executorType;

        // 创建我们的代理，以扩展更多功能
        sessionProxy = (SqlSession) Proxy.newProxyInstance(getClass()
                        .getClassLoader(),
                new Class[]{SqlSession.class},
                new SqlSessionProxy()
        );
    }

    @Override
    public <T> T selectOne(String statement) {
        return sessionProxy.selectOne(statement);
    }

    @Override
    public <T> T selectOne(String statement, Object parameter) {
        return sessionProxy.selectOne(statement, parameter);
    }

    @Override
    public <E> List<E> selectList(String statement) {
        return sessionProxy.selectList(statement);
    }

    @Override
    public <E> List<E> selectList(String statement, Object parameter) {
        return sessionProxy.selectList(statement, parameter);
    }

    @Override
    public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
        return sessionProxy.selectList(statement, parameter, rowBounds);
    }

    @Override
    public <K, V> Map<K, V> selectMap(String statement, String mapKey) {
        return sessionProxy.selectMap(statement, mapKey);
    }

    @Override
    public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey) {
        return sessionProxy.selectMap(statement, mapKey, mapKey);
    }

    @Override
    public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
        return sessionProxy.selectMap(statement, mapKey, mapKey, rowBounds);
    }

    @Override
    public <T> Cursor<T> selectCursor(String statement) {
        // return sessionProxy.selectCursor(statement);
        throw new UnsupportedOperationException("mybatis-magic 不支持的方法");
    }

    @Override
    public <T> Cursor<T> selectCursor(String statement, Object parameter) {
        // return sessionProxy.selectCursor(statement, parameter);
        throw new UnsupportedOperationException("mybatis-magic 不支持的方法");
    }

    @Override
    public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {
        // return sessionProxy.selectCursor(statement);
        throw new UnsupportedOperationException("mybatis-magic 不支持的方法");
    }

    @Override
    public void select(String statement, Object parameter, ResultHandler handler) {
        sessionProxy.select(statement, parameter, handler);
    }

    @Override
    public void select(String statement, ResultHandler handler) {
        sessionProxy.select(statement, handler);
    }

    @Override
    public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
        sessionProxy.select(statement, parameter, rowBounds, handler);
    }

    @Override
    public int insert(String statement) {
        return sessionProxy.insert(statement);
    }

    @Override
    public int insert(String statement, Object parameter) {
        return sessionProxy.insert(statement, parameter);
    }

    @Override
    public int update(String statement) {
        return sessionProxy.update(statement);
    }

    @Override
    public int update(String statement, Object parameter) {
        return sessionProxy.update(statement, parameter);
    }

    @Override
    public int delete(String statement) {
        return sessionProxy.delete(statement);
    }

    @Override
    public int delete(String statement, Object parameter) {
        return sessionProxy.delete(statement, parameter);
    }

    @Override
    public void commit() {
        throw new UnsupportedOperationException("不支持的方法，已经交由 final-magic 管理事务");
    }

    @Override
    public void commit(boolean force) {
        throw new UnsupportedOperationException("不支持的方法，已经交由 final-magic 管理事务");
    }

    @Override
    public void rollback() {
        throw new UnsupportedOperationException("不支持的方法，已经交由 final-magic 管理事务");
    }

    @Override
    public void rollback(boolean force) {
        throw new UnsupportedOperationException("不支持的方法，已经交由 final-magic 管理事务");
    }

    @Override
    public List<BatchResult> flushStatements() {
        return sessionProxy.flushStatements();
    }

    @Override
    public void close() {
        throw new UnsupportedOperationException("不支持的方法，已经交由 final-magic 管理连接，不需要您关闭回话");
    }

    @Override
    public void clearCache() {
        sessionProxy.clearCache();
    }

    @Override
    public Configuration getConfiguration() {
        // 从会话工厂中获取
        return sqlSessionFactory.getConfiguration();
    }

    @Override
    public <T> T getMapper(Class<T> type) {
        return getConfiguration().getMapper(type, this);
    }

    @Override
    public Connection getConnection() {
        SqlSession session = sqlSessionFactory.openSession(executorType);
        log.debug("获取的 Connection 需要手动关闭");
        return session.getConnection();
    }

    private class SqlSessionProxy implements InvocationHandler {

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 例如创建时将 session 存储到线程变量中，线程处理结束就关闭此会话
            SqlSession session = sqlSessionFactory.openSession(executorType);
            log.debug("获取连接: {}",session.toString());
            // 如果存在事务时，开启事务
            // session.getConnection().setAutoCommit(false);
            Object result = null;
            try {
                result = method.invoke(session, args);
                // 事务处理 如果不存在事务就提交操作
                // AOP 结束、 请求处理结束、提交事务
            } catch (Exception e) {
                // 事务处理 如果存在事务就要回滚
                // session.rollback();
                if (TransactionManage.isTransaction()) {
                    TransactionManage.rollback();
                }
                throw e;
            } finally {
                // 判断是否存在事务，不存在就关闭连接
                if (!TransactionManage.isTransaction()) {
                    session.close();
                    log.debug("关闭连接");
                }
            }
            return result;

        }
    }
}
